﻿//======================================================================
//
//	Cross-Platform XNA Menu System
//  http://cpxms.codeplex.com/
//
//  All code and content licensed under Microsoft Public License (Ms-PL)	
//
//======================================================================
//
//	File:		ItemProperties.cs
//
//	Author:		MJP
//
//	Date:		04/12/09
//
//======================================================================

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;
using System.Windows.Forms;
using System.Drawing;
using System.Diagnostics;
using System.Xml.Serialization;
using System.IO;
using System.Reflection;
using System.ComponentModel;

using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;

using CPXMenu;

using CPXMenuEditor.Forms;

using CPXMenuItem = CPXMenu.MenuItem;

namespace CPXMenuEditor.Controls
{
	/// <summary>
	/// Hosts the PropertyGrid control, and hooks events to provide
	/// Undo/Redo support through the ActionManager
	/// </summary>
    public partial class ItemProperties : UserControl
    {
        private MainFrm mainForm;
        private MenuEditorView menuEditorView;
        private MenuTree menuTree;
        private List<CPXMenuItem> selectedItems = new List<CPXMenuItem>();
        private ActionManager actionManager;
        private object[] oldValues;

		/// <summary>
		/// Gets the collection of currently selected items
		/// </summary>
        public ReadOnlyCollection<CPXMenuItem> SelectedItems
        {
            get 
            {
                return selectedItems.AsReadOnly();
            }
        }

		/// <summary>
		/// Gets the PropertyGrid control
		/// </summary>
        public PropertyGrid PropertyGrid
        {
            get { return propertyGrid; }
        }

		/// <summary>
		/// Creates a new instance of ItemProperties
		/// </summary>
        public ItemProperties()
        {
            InitializeComponent();

            propertyGrid.PropertyValueChanged += new PropertyValueChangedEventHandler(propertyGrid_PropertyValueChanged);
            propertyGrid.SelectedGridItemChanged += new SelectedGridItemChangedEventHandler(propertyGrid_SelectedGridItemChanged);
        }            

		/// <summary>
		/// Initializes the Control
		/// </summary>
		/// <param name="mainForm">The MainFrm of the app</param>
        public void Initialize(MainFrm mainForm)
        {
            this.mainForm = mainForm;
            menuEditorView = mainForm.MenuEditorView;
            menuTree = mainForm.MenuTree;
            actionManager = mainForm.ActionManager;            

            actionManager.UndoOccurred += new ActionDelegate(actionManager_UndoOccurred);
            actionManager.RedoOccurred += new ActionDelegate(actionManager_RedoOccurred);
            
            actionManager.SelectionChanged += new ActionManagerDelegate(actionManager_SelectionChanged);
        }
        
		/// <summary>
		/// Adds a item to the items selected on the PropertyGrid
		/// </summary>
		/// <param name="item">The item to add to the selected items</param>
        public void AddSelectedItem(CPXMenuItem item)
        {
            selectedItems.Add(item);
            SetPropertyGrid();
        }        

		/// <summary>
		/// Sets an item as the currently selected item on the PropertyGrid
		/// </summary>
		/// <param name="item">The item to set as selected</param>
        public void SetSelectedItem(CPXMenuItem item)
        {
            CPXMenuItem[] items = new CPXMenuItem[1];
            items[0] = item;
            SetSelectedItems(items);
        }

		/// <summary>
		/// Sets a group of items as the currently selected items on the PropertyGrid
		/// </summary>
		/// <param name="items">The items to set as selected</param>
        public void SetSelectedItems(CPXMenuItem[] items)
        {
            selectedItems.Clear();
            selectedItems.AddRange(items);
            SetPropertyGrid();
        }

		/// <summary>
		/// Removes an item from the collection of currently selected items
		/// </summary>
		/// <param name="item">The item to remove from the selected items collection</param>
        public void RemoveSelectedItem(CPXMenuItem item)
        {
            if (selectedItems.Contains(item))
            {
                selectedItems.Remove(item);
                SetPropertyGrid();
            }
            else
                Debug.Assert(false);
        }

		/// <summary>
		/// Clears all currently selected items
		/// </summary>
        public void ClearSelectedItems()
        {
            selectedItems.Clear();
            SetPropertyGrid();
        }

		/// <summary>
		/// Sets all currently selected items on the PropertyGrid
		/// </summary>
        private void SetPropertyGrid()
        {
            propertyGrid.SelectedObjects = null;
            object[] objects = new object[selectedItems.Count];
            for (int i = 0; i < selectedItems.Count; i++)
                objects[i] = selectedItems[i];
            propertyGrid.SelectedObjects = objects;

            SetOldValues();
        }

        private GridItem GetRootReferenceGridItem(GridItem gridItem)
        {
            Debug.Assert(gridItem.Parent != null && gridItem.PropertyDescriptor != null);

            GridItem rootItem = gridItem;
            if (!rootItem.PropertyDescriptor.ComponentType.IsValueType)
                return rootItem;

            while (gridItem.Parent.Parent != null)
            {
                gridItem = gridItem.Parent;
                if (gridItem.PropertyDescriptor != null && !gridItem.PropertyDescriptor.ComponentType.IsValueType)
                {
                    rootItem = gridItem;
                    break;
                }
            }

            Debug.Assert(!gridItem.PropertyDescriptor.ComponentType.IsValueType);
            return rootItem;
        }

        private void SetOldValues()
        {
            GridItem gridItem = propertyGrid.SelectedGridItem;

            if (gridItem != null && gridItem.GridItemType == GridItemType.Property)
            {
                gridItem = GetRootReferenceGridItem(gridItem);

                oldValues = new object[selectedItems.Count];
                for (int i = 0; i < oldValues.Length; i++)
                    oldValues[i] = gridItem.Value;
            }
            else
                oldValues = null;
        }

        void actionManager_RedoOccurred(ActionManager manager, IEditAction action)
        {
            if (action is PropertyEditAction)
                SetPropertyGrid();
        }

        void actionManager_UndoOccurred(ActionManager manager, IEditAction action)
        {
            if (action is PropertyEditAction)
                SetPropertyGrid();
        }

        void actionManager_SelectionChanged(ActionManager manager)
        {
            SetSelectedItems(manager.SelectedItemsArray);
        }        

        void propertyGrid_SelectedGridItemChanged(object sender, SelectedGridItemChangedEventArgs e)
        {
            if (e.NewSelection.PropertyDescriptor == null 
                || e.OldSelection == null
                || e.OldSelection.PropertyDescriptor == null 
                || e.NewSelection.PropertyDescriptor.Name != e.OldSelection.PropertyDescriptor.Name)
                SetOldValues();
        }

        void propertyGrid_PropertyValueChanged(object s, PropertyValueChangedEventArgs e)
        {
            object[] items = new object[selectedItems.Count];

            // Trace backwards through the chain of properties until we find
            // the first property
            List<string> propertyChain = new List<string>();
            GridItem gridItem = GetRootReferenceGridItem(e.ChangedItem);
            string propertyName = gridItem.PropertyDescriptor.Name;

            while (gridItem.Parent.Parent != null)
            {
                gridItem = gridItem.Parent;
                if (gridItem.PropertyDescriptor != null)
                    propertyChain.Add(gridItem.PropertyDescriptor.Name);                              
            }

            // Now walk the chain and find the owner of the property or field that was modified
            for (int i = 0; i < selectedItems.Count; i++)
            {                
                items[i] = selectedItems[i];
                object nextItem = items[i];
                for (int j = propertyChain.Count - 1; j >= 0; j--)                
                    items[i] = ActionManager.GetPropertyOrFieldValue(items[i], propertyChain[j]);                
            }

            actionManager.PropertyValueChanged(items, propertyName, oldValues);

            SetOldValues();
        }     
    }
}
